PropertyBinder = class('PropertyBinder')
PropertyBinder.ID = 1

local function getProperty(viewModel, path)
    local property = viewModel
    local propertyPath = {}
    for i = 1, #path - 1 do
        local name = path[i]
        local bindableProperty = property[name]
        if bindableProperty == nil then
            return propertyPath, nil
        end

        if type(bindableProperty) ~= 'table' then
            return propertyPath, nil
        end

        if bindableProperty.OnValueChanged == nil then
            return propertyPath, nil
        end

        table.insert(propertyPath, bindableProperty)
        property = bindableProperty.Value
    end
    local name = path[#path]
    local targetProperty = nil
    if property then
        targetProperty = property[name]
    end
    return propertyPath, targetProperty
end

function PropertyBinder:ctor(view)
    self.view = view
    self.triggerMode = false
    self.IDToEntry = {}
    self._binders = {} --function(viewModel)
    self._unbinders = {} --function(viewModel)
end

function PropertyBinder:Add(name, valueChangedHandler)
    local entry = {}
    entry.valueChangedHandler = valueChangedHandler

    local _internalOnValueChangeHandler = function(oldValue, newValue)
        if self.triggerMode then
            entry.newValue = newValue
        else
            valueChangedHandler(oldValue, newValue)
        end
    end

    local registerFunc = function(viewModel, bindableProperty)
        local value = nil
        if bindableProperty then
            table.insert(bindableProperty.OnValueChanged, _internalOnValueChangeHandler)
            value = bindableProperty.Value
        end

        _internalOnValueChangeHandler(nil, value) --初始化数据
    end

    local unregisterFunc = function(viewModel, bindableProperty)
        if bindableProperty then
            table.remove_value(bindableProperty.OnValueChanged, _internalOnValueChangeHandler)
        end
    end

    self:RegisterEvent(registerFunc, unregisterFunc, name)

    local id = PropertyBinder.ID
    PropertyBinder.ID = PropertyBinder.ID + 1
    self.IDToEntry[id] = entry
    return id
end

function PropertyBinder:AddEx(name, onAdd, onInsert, onRemove)--给list用绑定
    local registerFunc = function(viewModel, bindableProperty)
        if bindableProperty.AddHandlers then
            table.insert(bindableProperty.AddHandlers, onAdd)
        end
        if bindableProperty.InsertHandlers then
            table.insert(bindableProperty.InsertHandlers, onInsert)
        end
        if bindableProperty.RemoveHandlers then
            table.insert(bindableProperty.RemoveHandlers, onRemove)
        end
    end

    local unregisterFunc = function(viewModel, bindableProperty)
        if bindableProperty.AddHandlers then
            table.remove_value(bindableProperty.AddHandlers, onAdd)
        end
        if bindableProperty.InsertHandlers then
            table.remove_value(bindableProperty.InsertHandlers, onInsert)
        end
        if bindableProperty.RemoveHandlers then
            table.remove_value(bindableProperty.RemoveHandlers, onRemove)
        end
    end

    self:RegisterEvent(registerFunc, unregisterFunc, name)
end

function PropertyBinder:RegisterEvent(eventRegisterHandler, eventUnregisterHandler, name)
    name = name or ''

    local bind = nil
    local unbind = nil
    local RebindGen = function(currentPath)
        local currentPathArray = string.split(currentPath, '.')
        return function(oldValue, newValue)
            if oldValue ~= nil then
                unbind(oldValue, currentPathArray)
            end
            bind(newValue, currentPathArray)
        end
    end

    local path = string.split(name, '.')
    local rebinders = {}
    for i = 1, #path - 1 do
        table.insert(rebinders, RebindGen(table.concat(path, '.', i + 1)))
    end

    bind = function(viewModel, currentPath)
        currentPath = currentPath or path
        local bindableProperties, targetValue = getProperty(viewModel, currentPath)
        for i, bindableProperty in ipairs(bindableProperties) do
            table.insert(bindableProperty.OnValueChanged, rebinders[i])
            viewModel = bindableProperty
        end
        eventRegisterHandler(viewModel, targetValue)
    end

    unbind = function(viewModel, currentPath)
        currentPath = currentPath or path
        local bindableProperties, targetValue = getProperty(viewModel, currentPath)
        for i, bindableProperty in ipairs(bindableProperties) do
            table.remove_value(bindableProperty.OnValueChanged, rebinders[i])
            viewModel = bindableProperty
        end
        eventUnregisterHandler(viewModel, targetValue)
    end

    table.insert(self._binders, bind)
    table.insert(self._unbinders, unbind)
end

function PropertyBinder:Bind(viewModel)
    if viewModel then
        for _, binder in pairs(self._binders) do
            binder(viewModel)
        end
    end
end

function PropertyBinder:Unbind(viewModel)
    if viewModel then
        for _, unbinder in pairs(self._unbinders) do
            unbinder(viewModel)
        end
    end
end

function PropertyBinder:Trigger(id)
    if not self.triggerMode then return end
    local entry = self.IDToEntry[id]
    if not entry then return end
    entry.valueChangedHandler(entry.oldValue, entry.newValue)
    entry.oldValue = entry.newValue
end

function PropertyBinder:TriggerAll()
    if not self.triggerMode then return end
    for id, entry in pairs(self.IDToEntry) do
        if not entry then return end
        entry.valueChangedHandler(entry.oldValue, entry.newValue)
        entry.oldValue = entry.newValue
    end
end